<template>
  <div :class="classes">
    <div class="w-flex grow" :class="column ? 'column' : 'align-center'">
      <slot></slot>
    </div>
    <w-transition-expand y
      ><template v-if="Validation.message">
        <div
          class="w-form-el__error error w-form-el__error w-form-el__error"
          v-if="$slots['error-message']"
        >
          <slot name="error-message" :message="Validation.message"></slot>
        </div>
        <div
          class="w-form-el__error error w-form-el__error w-form-el__error"
          v-else
          v-html="Validation.message"
        ></div> </template
    ></w-transition-expand>
  </div>
</template>

<script>
  import {
    ref,
    reactive,
    computed,
    onMounted,
    onBeforeUnmount,
    watch,
    getCurrentInstance,
    inject,
  } from "vue";
  export default {
    name: "w-form-element",

    props: {
      valid: { required: true },
      disabled: { type: Boolean },
      readonly: { type: Boolean },
      inputValue: { required: true }, // The form element's input value.
      validators: { type: Array },
      isFocused: { default: false }, // Watched.
      column: { default: false }, // Flex direction of the embedded component: column or row by default.
    },

    emits: ["reset", "update:valid"],
    setup(props, ctx) {
      const instance = getCurrentInstance();

      const validateElement = inject("validateElement", { default: null });
      const formRegister = inject("formRegister", { default: null });
      const formUnregister = inject("formUnregister", { default: null });
      const noKeyupValidation = inject("noKeyupValidation", { default: false });
      const noBlurValidation = inject("noBlurValidation", { default: false });

      const Validation = reactive({
        isValid: null, // Null is pristine (unknown), can also be true or false.
        message: "", // Updated on w-form validation.
      });
      const hasJustReset = ref(false);
      const classes = computed(() => {
        const classes = [
          "w-form-el--error error",
          "w-form-el--success",
          "w-form-el--pristine",
        ];
        return [
          "w-form-el",
          classes[Validation.isValid === null ? 2 : ~~Validation.isValid],
        ];
      });

      function reset() {
        ctx.emit("reset"); // Notify parent to reset its input value.
        ctx.emit("update:valid", null); // Notify parent that this field is pristine again.
        Validation.message = ""; // Remove the error message.
        Validation.isValid = null; // Reset the element to pristine.
        hasJustReset.value = true;
      }

      // Allow triggering a particular field validation manually via `$refs.myField.validate()`.
      function validate() {
        ctx.emit("update:valid", validateElement(instance.ctx));
      }

      watch(
        () => props.inputValue,
        () => {
          if (hasJustReset.value) return (hasJustReset.value = false);

          // Update the form element's validity on input value change.
          if (!noKeyupValidation && props.validators) {
            ctx.emit("update:valid", validateElement(instance.ctx));
          }
        }
      );

      watch(
        () => props.isFocused,
        (val) => {
          // When focusing, reset the hasJustReset flag so the input value is watched again.
          if (val) {
            hasJustReset.value = false;
          }
          // On blur, Update the form element's validity.
          else if (!noBlurValidation && props.validators) {
            ctx.emit("update:valid", validateElement(instance.ctx));
          }
        }
      );

      onMounted(() => {
        if (formRegister) {
          formRegister(instance.ctx);
        }
      });
      onBeforeUnmount(() => {
        if (formUnregister) {
          formUnregister(instance.ctx);
        }
      });

      return {
        Validation,
        hasJustReset,
        classes,
        //methods
        reset,
        validate,
      };
    },
  };
</script>